package com.xxx.client.handler;

import com.xxx.client.registry.ChannelInfo;
import com.xxx.codec.RpcEncoder;
import com.xxx.request.RpcRequest;
import com.xxx.serializer.FastJsonSerializer;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.string.StringDecoder;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.UUID;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * Created by louisyuu on 2020/4/30 上午10:16
 */
public class RemoteCallHandler implements InvocationHandler {

    private static ExecutorService executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());


    /**
     * Remote service class
     */
    private Class<?> serviceClass;

    /**
     * Remote method full name
     */
    private String targetMethodName;


    /**
     * Channel info include remote connect address
     */
    private ChannelInfo channelInfo;


    public RemoteCallHandler(Class<?> serviceClass, String targetMethodName, ChannelInfo channelInfo) {
        this.serviceClass = serviceClass;
        this.targetMethodName = targetMethodName;
        this.channelInfo = channelInfo;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        ChannelBoundHandler channelBoundHandler = new ChannelBoundHandler();
        /**
         * 初始化netty client
         */
        initNettyClient(channelInfo, channelBoundHandler);


        RpcRequest rpcRequest = new RpcRequest();
        rpcRequest.setRequestId(UUID.randomUUID().toString());
        rpcRequest.setClassName(serviceClass.getName());
        rpcRequest.setMethodName(targetMethodName);

        Class<?>[] parameterTypes = new Class[args.length];

        for (int i = 0; i < args.length; i++) {
            parameterTypes[i] = args[i].getClass();
        }
        rpcRequest.setParameterTypes(parameterTypes);
        rpcRequest.setParameters(args);

        // 设置参数
        channelBoundHandler.setParam(rpcRequest);
        // 去服务端请求数据

        return executor.submit(channelBoundHandler).get();
    }


    public static void initNettyClient(ChannelInfo channelInfo, ChannelBoundHandler channelBoundHandler) {

        EventLoopGroup group = new NioEventLoopGroup();
        Bootstrap bootstrap = new Bootstrap();
        bootstrap.group(group)
                .channel(NioSocketChannel.class)
                .option(ChannelOption.TCP_NODELAY, true)
                .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 5000)
                .handler(new ChannelInitializer<SocketChannel>() {
                    protected void initChannel(SocketChannel ch) throws Exception {
                        ChannelPipeline pipeline = ch.pipeline();
                        pipeline.addLast(new RpcEncoder(RpcRequest.class, new FastJsonSerializer()));
                        pipeline.addLast(new StringDecoder());
                        pipeline.addLast(channelBoundHandler);
                    }
                });

        try {

            ChannelFuture channelFuture = bootstrap
                    .connect(channelInfo.getServerInfo().getHost(), channelInfo.getServerInfo().getPort()).sync();

            if (channelFuture.isDone() && channelFuture.isSuccess()) {
                System.out.println("======Socket established");
                if (channelFuture.channel().isActive()) {
                    channelInfo.setChannelFuture(channelFuture);
                } else {
                }

            }

        } catch (InterruptedException e) {
            e.printStackTrace();
        }


    }


}
